/*
 * Estadistica.cpp
 *
 *  Created on: 01/06/2013
 *      Author: npersia
 */

#include "Estadistica.h"

Estadistica::Estadistica() {
	this->titulosBuscados = new list<DatoContable*>;
	this->autoresBuscados = new list<DatoContable*>;
	this->frasesBuscadas = new list<DatoContable*>;

}

void Estadistica::agregarTitulo(string unTitulo) {
	this->agregarDato(titulosBuscados, unTitulo);
}

void Estadistica::agregarAutor(string unAutor) {
	this->agregarDato(autoresBuscados, unAutor);
}

void Estadistica::agregarfrase(string unaFrase) {
	this->agregarDato(frasesBuscadas, unaFrase);
}

list<DatoContable*>* Estadistica::getTitulos() {
	return this->titulosBuscados;
}

list<DatoContable*>* Estadistica::getAutores() {
	return this->autoresBuscados;
}

list<DatoContable*>* Estadistica::getFrases() {
	return this->frasesBuscadas;
}

//Agrega un dato a la lista siempre y cuando no se haya agregado anteriormente
//Pos1: Si el dato no se encuentra en la lista, se agranda la lista
//Pos2: Si el dato se encuentra en la lista, se aumenta el contador del dato
void Estadistica::agregarDato(list<DatoContable*>* unaLista, string nuevoDato) {
	bool encontrado = false;
	list<DatoContable*>::iterator iteradorLista = unaLista->begin();

	while(iteradorLista != unaLista->end())
	{
		DatoContable * unDato = *iteradorLista;
		if(*unDato==nuevoDato)
		{
			unDato->aumentarContador();
			encontrado = true;
		}
		iteradorLista++;
	}

	if(!encontrado)
	{
		StringContable * nuevoDatoString = new StringContable(nuevoDato);
		nuevoDatoString->aumentarContador();
		unaLista->push_back(nuevoDatoString);
	}
}


//Pre:
//Pos1: Se devuelve una lista con los elementos con el valor mas alto en el contador.
//Pos2: La lista es creada de forma dinamica. Debe ser eliminada con el destructor de lista.
list<DatoContable*>* Estadistica::getMayores(list<DatoContable*>* unaLista) {

	list<DatoContable*> * listaResultado  = new list<DatoContable*>;
	if(unaLista->size() == 0)
		return listaResultado;


	//consigo el contador mas alto
	unaLista->sort(DatoContable::ordenarPorContador);

	DatoContable* unDatoMayor = unaLista->back();
	unsigned short int mayorContador = unDatoMayor->getContador();

	list<DatoContable*>::reverse_iterator iteradorLista = unaLista->rbegin();
	while(iteradorLista != unaLista->rend() && (*iteradorLista)->getContador()== mayorContador)
	{
		listaResultado->push_back(*iteradorLista);
		++iteradorLista;
	}

	return listaResultado;

}

//Elimina el contenido de la lista y la referencia a ella
void Estadistica::eliminarLista(list<DatoContable*>* listaAEliminar) {
	list<DatoContable*>::iterator iteradorLista = listaAEliminar->begin();

	while(iteradorLista != listaAEliminar->end())
	{
		DatoContable* datoAEliminar = *iteradorLista;
		delete datoAEliminar;
		iteradorLista++;
	}
	delete listaAEliminar;
}


void Estadistica::mostrarLista(list<DatoContable*>* unaLista) {
	list<DatoContable*>::iterator iteradorLista = unaLista->begin();

	while(iteradorLista != unaLista->end())
	{
		DatoContable* unDato = *iteradorLista;
		string unDatoString;
		unDato->getDato(unDatoString);
		cout << unDatoString << endl;
		iteradorLista++;
	}

}


void Estadistica::mostrarTitulosMasBuscados() {

	list<DatoContable*> * titulosMasBuscados  = this->getMayores(titulosBuscados);

	cout<<endl;
	cout<<"TITULOS MAS BUSCADOS"<<endl;

	if(titulosBuscados->size() == 0)
	{
		cout << "-Aun no sea han buscado titulos-"<<endl;
	}else
	{
		mostrarLista(titulosMasBuscados);
		unsigned short int contadorDeBusquedas = titulosMasBuscados->front()->getContador();
		cout<<"-Con un total de "<< contadorDeBusquedas <<" busquedas realizadas-"<<endl;
		cout <<endl;
	}

	delete titulosMasBuscados;
}

void Estadistica::mostrarAutoresMasBuscados() {
	list<DatoContable*> * autoresMasBuscados  = this->getMayores(autoresBuscados);

	cout<<endl;
	cout<<"AUTORES MAS BUSCADOS"<<endl;

	if(autoresBuscados->size() == 0)
	{
		cout << "-Aun no sea han buscado autores-"<<endl;
	}else
	{
		mostrarLista(autoresMasBuscados);
		unsigned short int contadorDeBusquedas = autoresMasBuscados->front()->getContador();
		cout<<"-Con un total de "<< contadorDeBusquedas <<" busquedas realizadas-"<<endl;
		cout <<endl;
	}

	delete autoresMasBuscados;
}

void Estadistica::mostrarFrasesMasBuscadas() {
	list<DatoContable*> * frasesMasBuscadas  = this->getMayores(frasesBuscadas);

	cout<<endl;
	cout<<"FRASES MAS BUSCADAS"<<endl;

	if(frasesBuscadas->size() == 0)
	{
		cout << "-Aun no sea han buscado frases-"<<endl;
	}else
	{
		mostrarLista(frasesMasBuscadas);
		unsigned short int contadorDeBusquedas = frasesMasBuscadas->front()->getContador();
		cout<<"-Con un total de "<< contadorDeBusquedas <<" busquedas realizadas-"<<endl;
		cout <<endl;
	}

	delete frasesMasBuscadas;
}


//Se devolveran los temas que mas se hayan involucrado en los 3 tipos de busquedas.
//Se utiliza una BibliotecaDeCanciones para averiguar los ids unicos de cada termino buscado de cada categoria de busqueda
//Luego estos ids se cruzan entre categorias, quedando asi, solo los ids que hayan tenido relevancia en las tres categorias.
void Estadistica::mostrarTemasMasCoincidentes(BiblioDeCanciones * unaBiblioteca) {

	ArbolBMas * unIndiceAutores = unaBiblioteca->getIndiceAutores();
	HashExtensible * unIndiceTitulos = unaBiblioteca->getIndiceTitulos();
	IndiceInvertido * unIndiceInvertido = unaBiblioteca->getIndiceInvertido();

	list<DatoContable*>::iterator itTitulos = titulosBuscados->begin();
	list<DatoContable*>::iterator itAutores = autoresBuscados->begin();
	list<DatoContable*>::iterator itFrases = frasesBuscadas->begin();

	//Si no se realizaron consultas en alguna de las tres categorias, entonces no puede haber coincidencias
	if(titulosBuscados->size() == 0 || autoresBuscados->size() == 0 || frasesBuscadas->size() == 0)
	{
		cout<<endl;
		cout<<"TEMAS COINCIDENTES EN LOS TRES TIPOS DE BUSQUEDAS"<<endl;
		cout << "-No se pueden mostrar temas que coincidan en todas las busquedas-"<<endl;
		if(frasesBuscadas->size() == 0)
			cout << "-Aun no sea han buscado frases-"<<endl;
		if(autoresBuscados->size() == 0)
			cout << "-Aun no sea han buscado autores-"<<endl;
		if(titulosBuscados->size() == 0)
			cout << "-Aun no sea han buscado titulos-"<<endl;

		return;
	}

	list<DatoContable*> * idsTitulos = new list<DatoContable*>; //Contendra los ids de todos los titulos
	list<DatoContable*> * idsAutores = new list<DatoContable*>; //contendra los ids de todos los autores
	list<DatoContable*> * idsFrases = new list<DatoContable*>; //contendra los ids de todas las frases
	list<unsigned int> * ids; //Se utilizara como auxiliar en cada busqueda de ids
	list<DatoContable*> * idsConContador; //Se utilizara como auxiliar en cada busqueda de ids conteniendo ids y contador de busqueda de cada elemento
	list<DatoContable*>  * idsAutoresTitulos; //Se utilizara para contener los ids que tengan en comun los titulos y los autores
	list<DatoContable*>  * idsAutoresTitulosFrases; //Se utilizara como lista resultado conteniendo los ids que coincidieron en las 3 busquedas y el menor contador de esas 3 busquedas


	//Se extrae todos los ids de todos los titulos mas buscados
	while(itTitulos != titulosBuscados->end())
	{
		string unTitulo;
		(*itTitulos)->getDato(unTitulo);
		ids = unIndiceTitulos->BuscarClave(unTitulo);

		if(!ids)
			ids = new list<unsigned int>;

		idsConContador = agregarContador(ids, (*itTitulos)->getContador() );
		this->conjuncion(idsTitulos,idsConContador);
		delete ids;
		delete idsConContador;
		itTitulos++;
	}

	//Se extrae todos los ids de todos los autores mas buscados
	while(itAutores != autoresBuscados->end())
	{
		string unAutor;
		(*itAutores)->getDato(unAutor);
		ids = unIndiceAutores->buscarClave(unAutor);

		if(!ids)
			ids = new list<unsigned int>;

		idsConContador = agregarContador(ids, (*itAutores)->getContador() );
		this->conjuncion(idsAutores,idsConContador);
		delete ids;
		delete idsConContador;
		itAutores++;
	}

	//Se extraen los ids repetidos
	this->unicidad(idsAutores);

	//Se hace la conjuncion entre las dos listas, quedando como resultado una lista con los ids coincidentes
	idsAutoresTitulos = this->disyuncion(idsTitulos,idsAutores);
	eliminarLista(idsTitulos);
	eliminarLista(idsAutores);

	//Si hubo coincidencias, se continua.
	if(idsAutoresTitulos->size() > 0 )
	{
		//Se extrae todos los ids de todas las frases mas buscadas
		while(itFrases != frasesBuscadas->end())
		{
			string unaFrase;
			(*itFrases)->getDato(unaFrase);
			ids = unIndiceInvertido->BuscarFrase(unaFrase);

			if(!ids)
				ids = new list<unsigned int>;

			idsConContador = agregarContador(ids, (*itFrases)->getContador() );
			this->conjuncion(idsFrases,idsConContador);
			delete ids;
			delete idsConContador;
			itFrases++;
		}

		//Se extraen los ids repetidos
		this->unicidad(idsFrases);

	}

	//Se hace la conjuncion entre las dos listas, quedando como resultado una lista con los ids coincidentes
	idsAutoresTitulosFrases = this->disyuncion(idsAutoresTitulos,idsFrases);
	eliminarLista(idsFrases);
	eliminarLista(idsAutoresTitulos);

	list<DatoContable*> * idsTemasCoincidentes  = this->getMayores(idsAutoresTitulosFrases);
	list<DatoContable*>::iterator itAutoresTitulosFrases = idsTemasCoincidentes->begin();

	cout<<endl;
	cout<<"TEMAS COINCIDENTES EN LOS TRES TIPOS DE BUSQUEDAS"<<endl;
	//Si hubo coincidencias, se continua.
	if(idsTemasCoincidentes->size() == 0)
	{
		cout<<"-No se encontraron temas que hayan coincidido en los tres tipos de busqueda-"<<endl;
	}
	else
	{
		//Muestro por pantalla los Ids y su respectiva descripcion
		while(itAutoresTitulosFrases != idsTemasCoincidentes->end())
		{
			unsigned int unId;
			(*itAutoresTitulosFrases)->getDato(unId);
			string * cancion = unaBiblioteca->getRegistroCanciones()->Buscar(unId);
			string auxiliar = "";
			auxiliar = cancion->substr(0,cancion->find("\n"));
			cout << "ID : " << unId << endl << auxiliar << endl;
			delete cancion;
			unsigned short int contadorDeBusquedas = (*itAutoresTitulosFrases)->getContador();
			cout<<"-Coincidiendo "<< contadorDeBusquedas <<" veces-"<<endl;
			cout <<endl;
			itAutoresTitulosFrases++;
		}

	}
delete idsTemasCoincidentes;
eliminarLista(idsAutoresTitulosFrases);

}



//Pos: devuelve el menor entre tres numeros
unsigned short int Estadistica::menor(unsigned short int a,	unsigned short int b, unsigned short int c) {

	if(a<=b)
	{
		if(a<=c)
		{
			return a;
		}else
		{
			return c;
		}
	}else
	{
		if(b<=c)
		{
			return b;
		}else
		{
			return c;
		}
	}

}


Estadistica::~Estadistica() {
	eliminarLista(titulosBuscados);
	eliminarLista(autoresBuscados);
	eliminarLista(frasesBuscadas);
}


//Pre: Para utilizar el menu de estadisticas, la biblioteca debe haber sido indexada. De no ser asi, la aplicacion posiblemente (seguramente)
//devendra en un cierre abrupto de la misma, derivando en una corrupcion de los archivos utilizados por la misma.
void Estadistica::ejecutarMenuEstadisticas(BiblioDeCanciones * unabiblioteca) {
	char input = '0';

		while ( input != '6') {
			this->imprimirMenuEstadisticas();
			cin >> input;
			switch ( input ) {
				case '1':
					this->mostrarTitulosMasBuscados();
					break;
				case '2':
					this->mostrarAutoresMasBuscados();
					break;
				case '3':
					this->mostrarFrasesMasBuscadas();
					break;
				case '4':
					this->mostrarTemasMasCoincidentes(unabiblioteca);
					break;
				case '5':
					this->mostrarTodasLasEstadisticas(unabiblioteca);
					break;
				default:
					cout << endl << endl;
					break;
			}
		}
}

void Estadistica::imprimirMenuEstadisticas() {
	cout<< endl;
	cout << "ESTADISTICAS" << endl << endl;
	cout << "Opciones:" << endl << endl;
	cout << "1-Titulos mas buscados" << endl;
	cout << "2-Autores mas buscados" << endl;
	cout << "3-Frases mas buscadas" << endl;
	cout << "4-Temas que mas coincidieron en las busquedas" << endl;
	cout << "5-Mostrar todas las estadisticas" << endl;
	cout << "6-Volver al Menu Principal" << endl;

}

//Pre:
//Pos1:Se remueven los elementos que se encuentran tanto en la listaA como en la listaB con el contador menor entre ellos
//Pos2: La lista es creada de forma dinamica. Debe ser eliminada con su destructor.
list<DatoContable*>* Estadistica::disyuncion(list<DatoContable*>* listaA,	list<DatoContable*>* listaB) {
	list<DatoContable*> * listaDisyuncion  = new list<DatoContable*>;

	if(listaA->size() == 0 || listaB->size() == 0)
		return listaDisyuncion;

	listaA->sort(DatoContable::ordenarPorDatoContable);
	listaB->sort(DatoContable::ordenarPorDatoContable);

	list<DatoContable*>::iterator iteradorListaA = listaA->begin();
	list<DatoContable*>::iterator iteradorListaB = listaB->begin();
	while(iteradorListaA != listaA->end() && iteradorListaB != listaB->end())
	{
		DatoContable* datoA = *iteradorListaA;
		DatoContable* datoB = *iteradorListaB;
		if( *(*iteradorListaA)==*iteradorListaB)
		{
			if((*iteradorListaA)->getContador() < (*iteradorListaB)->getContador())
			{
				listaDisyuncion->push_back(datoA);
				delete *iteradorListaB;
				iteradorListaA=listaA->erase(iteradorListaA);
				iteradorListaB=listaB->erase(iteradorListaB);

			}else
			{
				listaDisyuncion->push_back(datoB);
				delete *iteradorListaA;
				iteradorListaA=listaA->erase(iteradorListaA);
				iteradorListaB=listaB->erase(iteradorListaB);
			}

		}else if(*datoA<datoB)
		{
			iteradorListaA++;
		}else
		{
			iteradorListaB++;
		}
	}

	return listaDisyuncion;
}


//Pos: Se agregan al final de unaLista todos los elementos de unaListaAAcolar
void Estadistica::conjuncion(list<DatoContable*>* unaLista, list<DatoContable*>* unaListaAAcolar) {
	list<DatoContable*>::iterator itAcolar = unaListaAAcolar->begin();

	while(itAcolar != unaListaAAcolar->end())
	{
		unaLista->push_back(*itAcolar);
		itAcolar++;
	}
}

//Elimina de la lista los elementos que tengan datos iguales.
//Pos: Por cada par de elementos iguales, se elimina un elemento y se mantiene otro junto con un nuevo contador resultante de la suma de los
//contadores de los dos elementos.
void Estadistica::unicidad(list<DatoContable*>* unaLista) {

	unaLista->sort(DatoContable::ordenarPorDatoContable);

	list<DatoContable*>::iterator primerDato = unaLista->begin();
	list<DatoContable*>::iterator ultimoDato = unaLista->end();
	list<DatoContable*>::iterator proximoDato;

	if(primerDato == ultimoDato)
		return;

	proximoDato = primerDato;
	while(++proximoDato != ultimoDato)
	{
		if(*(*primerDato)==*proximoDato)
		{
			unsigned short int nuevoContador = (*primerDato)->getContador() + (*proximoDato)->getContador();
			(*primerDato)->setContador(nuevoContador);
			delete *proximoDato;
			unaLista->erase(proximoDato);
			proximoDato=primerDato;
		}
		else
		{
			primerDato=proximoDato;
			proximoDato=primerDato;
		}
	}

}

void Estadistica::mostrarTodasLasEstadisticas(BiblioDeCanciones * unaBiblioteca) {
	cout<< endl;
	cout << "RESUMEN DE TODAS LAS ESTADISTICAS" << endl << endl;
	this->mostrarTitulosMasBuscados();
	this->mostrarAutoresMasBuscados();
	this->mostrarFrasesMasBuscadas();
	this->mostrarTemasMasCoincidentes(unaBiblioteca);
}

//Pos1: Se devuelve una lista de tipo DatoContable con datos de tipo UnsignedIntContable creados Dinamicamente
//con el contador seteado con el valor pasado por parametro a la funcion
//Pos2: La lista se crea de forma dinámica. Debe eliminarse con el destructor eliminarLista() de esta clase.
list<DatoContable*>* Estadistica::agregarContador(list<unsigned int>* unaLista, unsigned short int unContador) {
	list<DatoContable*>* listaContable = new list<DatoContable*>;

	list<unsigned int>::iterator itLista = unaLista->begin();

	while(itLista != unaLista->end())
	{
		UnsignedIntContable * unDatoNuevo = new UnsignedIntContable(*itLista);
		unDatoNuevo->setContador(unContador);
		listaContable->push_back(unDatoNuevo);
		itLista++;
	}

	return listaContable;

}
